<?php
/**
 * WC_Shipping_ABF class.
 *
 * @extends WC_Shipping_Method
 */
class WC_Shipping_ABF extends WC_Shipping_Method {

  private $endpoint = 'https://www.abfs.com/xml/aquotexml.asp';

  private $package_types = array(
    'BAG'  => 'Bag',
    'BL'   => 'Bale',
    'BRL'  => 'Barrel',
    'BSK'  => 'Basket',
    'BX'   => 'Box',
    'BKT'  => 'Bucket',
    'BLKH' => 'Bulkhead',
    'BDL'  => 'Bundle',
    'CRB'  => 'Carboy',
    'CTN'  => 'Carton',
    'CS'   => 'Case',
    'CHT'  => 'Chest',
    'CL'   => 'Coil',
    'CRT'  => 'Crate',
    'CYL'  => 'Cylinder',
    'DR'   => 'Drum',
    'FIR'  => 'Firkin',
    'HMP'  => 'Hamper',
    'HHD'  => 'Hogshead',
    'KEG'  => 'Keg',
    'PKG'  => 'Package',
    'PL'   => 'Pail',
    'PLT'  => 'Pallet',
    'PC'   => 'Piece',
    'RK'   => 'Rack',
    'REL'  => 'Reel',
    'RL'   => 'Roll',
    'SKD'  => 'Skid',
    'SLP'  => 'Slip Sheet',
    'TOTE' => 'Tote',
    'TRK'  => 'Trunk',
    'TB'   => 'Tube',
  );

  private $commodity_classes = array(
    '50',
    '55',
    '60',
    '65',
    '70',
    '77.5',
    '85',
    '92.5',
    '100',
    '110',
    '125',
    '150',
    '175',
    '200',
    '250',
    '300',
    '400',
    '500',
  );

  /**
   * __construct function.
   *
   * @access public
   * @return void
   */
  public function __construct() {
    $this->id                 = 'abf';
    $this->method_title       = __( 'ABF', 'wc_abf' );
    $this->method_description = __( 'The <strong>ABF</strong> extension obtains rates dynamically from the ABF API during cart/checkout.', 'wc_abf' );
    $this->init();
  }

  /**
   * init function.
   *
   * @access public
   * @return void
   */
  private function init() {
    global $woocommerce;
    // Load the settings.
    $this->init_form_fields();
    $this->init_settings();

    // Define user set variables
    $this->enabled            = isset( $this->settings['enabled'] ) ? $this->settings['enabled'] : $this->enabled;
    $this->title            = isset( $this->settings['title'] ) ? $this->settings['title'] : $this->method_title;
    $this->availability        = isset( $this->settings['availability'] ) ? $this->settings['availability'] : 'all';
    $this->countries           = isset( $this->settings['countries'] ) ? $this->settings['countries'] : array();

    // API Settings
    $this->user_id             = isset( $this->settings['user_id'] ) ? $this->settings['user_id'] : '';
    $this->password            = isset( $this->settings['password'] ) ? $this->settings['password'] : '';
    $this->secure_id          = isset( $this->settings['secure_id'] ) ? $this->settings['secure_id'] : '';
    $this->origin_zip     = isset( $this->settings['origin_zip'] ) ? $this->settings['origin_zip'] : '';
    $this->origin_city     = isset( $this->settings['origin_city'] ) ? $this->settings['origin_city'] : '';
    $this->origin_country_state   = isset( $this->settings['origin_country_state'] ) ? $this->settings['origin_country_state'] : '';
    $this->debug              = isset( $this->settings['debug'] ) && $this->settings['debug'] == 'yes' ? true : false;

    // Services and Packaging
    $this->offer_rates      = isset( $this->settings['offer_rates'] ) ? $this->settings['offer_rates'] : 'all';
    $this->fallback         = ! empty( $this->settings['fallback'] ) ? $this->settings['fallback'] : '';
    $this->packing_method   = isset( $this->settings['packing_method'] ) ? $this->settings['packing_method'] : 'per_item';
    $this->default_commodity_class = isset( $this->settings['default_commodity_class'] ) ? $this->settings['default_commodity_class'] : '65';
    $this->boxes            = isset( $this->settings['boxes'] ) ? $this->settings['boxes'] : array();

    if (strstr($this->origin_country_state, ':')) :
      $this->origin_country = current(explode(':',$this->origin_country_state));
      $this->origin_state   = end(explode(':',$this->origin_country_state));
    else :
      $this->origin_country = $this->origin_country_state;
      $this->origin_state   = '';
    endif;

    add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );

  }

  /**
   * environment_check function.
   *
   * @access public
   * @return void
   */
  private function environment_check() {
    global $woocommerce;

    $error_message = '';

    // // Check for ABF User ID
    // if ( ! $this->user_id && $this->enabled == 'yes' ) {
    //   $error_message .= '<p>' . __( 'ABF is enabled, but the ABF User ID has not been set.', 'wc_abf' ) . '</p>';
    // }

    // // Check for ABF Password
    // if ( ! $this->password && $this->enabled == 'yes' ) {
    //   $error_message .= '<p>' . __( 'ABF is enabled, but the ABF Password has not been set.', 'wc_abf' ) . '</p>';
    // }

    // Check for ABF Access Key
    if ( ! $this->secure_id && $this->enabled == 'yes' ) {
      $error_message .= '<p>' . __( 'ABF is enabled, but the ABF Access Key has not been set.', 'wc_abf' ) . '</p>';
    }

    // Check for Origin City
    if ( ! $this->origin_city && $this->enabled == 'yes' ) {
      $error_message .= '<p>' . __( 'ABF is enabled, but the origin city has not been set.', 'wc_abf' ) . '</p>';
    }

    // Check for Origin ZIP
    if ( ! $this->origin_zip && $this->enabled == 'yes' ) {
      $error_message .= '<p>' . __( 'ABF is enabled, but the origin postcode has not been set.', 'wc_abf' ) . '</p>';
    }

    // Check for Origin country
    if ( ! $this->origin_country_state && $this->enabled == 'yes' ) {
      $error_message .= '<p>' . __( 'ABF is enabled, but the origin country/state has not been set.', 'wc_abf' ) . '</p>';
    }

    // If user has selected to pack into boxes,
    // Check a custom box is defined
    if ( ( $this->packing_method == 'box_packing' ) && ( $this->enabled == 'yes' ) ) {
      if ( empty( $this->boxes ) ){
        $error_message .= '<p>' . __( 'ABF is enabled, and Parcel Packing Method is set to \'Pack into boxes\', but no custom boxes are defined. Items will be packed individually.', 'wc_abf' ) . '</p>';
      }
    }

    if ( ! $error_message == '' ) {
      echo '<div class="error">';
      echo $error_message;
      echo '</div>';
    }


  }

  /**
   * admin_options function.
   *
   * @access public
   * @return void
   */
  public function admin_options() {
    // Check users environment supports this method
    $this->environment_check();

    // Show settings
    parent::admin_options();
  }

  /**
   *
   * generate_single_select_country_html function
   *
   * @access public
   * @return void
   */
  function generate_single_select_country_html() {
    global $woocommerce;

    ob_start();
    ?>
    <tr valign="top">
      <th scope="row" class="titledesc">
        <label for="origin_country"><?php _e( 'Origin Country', 'wc_abf' ); ?></label>
      </th>
      <td class="forminp"><select name="woocommerce_abf_origin_country_state" id="woocommerce_abf_origin_country_state" style="width: 250px;" data-placeholder="<?php _e('Choose a country&hellip;', 'woocommerce'); ?>" title="Country" class="chosen_select">
        <?php echo $woocommerce->countries->country_dropdown_options( $this->origin_country, $this->origin_state ? $this->origin_state : '*' ); ?>
      </select> <span class="description"><?php _e( 'Enter the country for the <strong>sender</strong>.', 'wc_abf' ) ?></span>
         </td>
       </tr>
    <?php
    return ob_get_clean();
  }

  

  /**
   * generate_box_packing_html function.
   *
   * @access public
   * @return void
   */
  public function generate_box_packing_html() {
    ob_start();
    ?>
    <tr valign="top" id="packing_options">
      <th scope="row" class="titledesc"><?php _e( 'Custom Boxes', 'wc_abf' ); ?></th>
      <td class="forminp">
        <style type="text/css">
          .abf_boxes td, .abf_services td {
            vertical-align: middle;
            padding: 4px 7px;
          }
          .abf_boxes td input {
            margin-right: 4px;
          }
          .abf_boxes .check-column {
            vertical-align: middle;
            text-align: left;
            padding: 0 7px;
          }
          .abf_services th.sort {
            width: 16px;
          }
          .abf_services td.sort {
            cursor: move;
            width: 16px;
            padding: 0;
            cursor: move;
            background: url() no-repeat center;         }
        </style>
        <table class="abf_boxes widefat">
          <thead>
            <tr>
              <th class="check-column"><input type="checkbox" /></th>
              <th><?php _e( 'Outer Length', 'wc_abf' ); ?></th>
              <th><?php _e( 'Outer Width', 'wc_abf' ); ?></th>
              <th><?php _e( 'Outer Height', 'wc_abf' ); ?></th>
              <th><?php _e( 'Inner Length', 'wc_abf' ); ?></th>
              <th><?php _e( 'Inner Width', 'wc_abf' ); ?></th>
              <th><?php _e( 'Inner Height', 'wc_abf' ); ?></th>
              <th><?php _e( 'Box Weight', 'wc_abf' ); ?></th>
              <th><?php _e( 'Max Weight', 'wc_abf' ); ?></th>
            </tr>
          </thead>
          <tfoot>
            <tr>
              <th colspan="3">
                <a href="#" class="button plus insert"><?php _e( 'Add Box', 'wc_abf' ); ?></a>
                <a href="#" class="button minus remove"><?php _e( 'Remove selected box(es)', 'wc_abf' ); ?></a>
              </th>
              <th colspan="6">
                <small class="description"><?php _e( 'Items will be packed into these boxes depending based on item dimensions and volume. Outer dimensions will be passed to ABF, whereas inner dimensions will be used for packing. Items not fitting into boxes will be packed individually.', 'wc_abf' ); ?></small>
              </th>
            </tr>
          </tfoot>
          <tbody id="rates">
            <?php
              if ( $this->boxes && ! empty( $this->boxes ) ) {
                foreach ( $this->boxes as $key => $box ) {
                  ?>
                  <tr>
                    <td class="check-column"><input type="checkbox" /></td>
                    <td><input type="text" size="5" name="boxes_outer_length[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['outer_length'] ); ?>" />IN</td>
                    <td><input type="text" size="5" name="boxes_outer_width[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['outer_width'] ); ?>" />IN</td>
                    <td><input type="text" size="5" name="boxes_outer_height[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['outer_height'] ); ?>" />IN</td>
                    <td><input type="text" size="5" name="boxes_inner_length[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['inner_length'] ); ?>" />IN</td>
                    <td><input type="text" size="5" name="boxes_inner_width[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['inner_width'] ); ?>" />IN</td>
                    <td><input type="text" size="5" name="boxes_inner_height[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['inner_height'] ); ?>" />IN</td>
                    <td><input type="text" size="5" name="boxes_box_weight[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['box_weight'] ); ?>" />LBS</td>
                    <td><input type="text" size="5" name="boxes_max_weight[<?php echo $key; ?>]" value="<?php echo esc_attr( $box['max_weight'] ); ?>" />LBS</td>
                  </tr>
                  <?php
                }
              }
            ?>
          </tbody>
        </table>
        <script type="text/javascript">

          jQuery(window).load(function(){

            jQuery('.abf_boxes .insert').click( function() {
              var $tbody = jQuery('.abf_boxes').find('tbody');
              var size = $tbody.find('tr').size();
              var code = '<tr class="new">\
                  <td class="check-column"><input type="checkbox" /></td>\
                  <td><input type="text" size="5" name="boxes_outer_length[' + size + ']" />IN</td>\
                  <td><input type="text" size="5" name="boxes_outer_width[' + size + ']" />IN</td>\
                  <td><input type="text" size="5" name="boxes_outer_height[' + size + ']" />IN</td>\
                  <td><input type="text" size="5" name="boxes_inner_length[' + size + ']" />IN</td>\
                  <td><input type="text" size="5" name="boxes_inner_width[' + size + ']" />IN</td>\
                  <td><input type="text" size="5" name="boxes_inner_height[' + size + ']" />IN</td>\
                  <td><input type="text" size="5" name="boxes_box_weight[' + size + ']" />LBS</td>\
                  <td><input type="text" size="5" name="boxes_max_weight[' + size + ']" />LBS</td>\
                </tr>';

              $tbody.append( code );

              return false;
            } );

            jQuery('.abf_boxes .remove').click(function() {
              var $tbody = jQuery('.abf_boxes').find('tbody');

              $tbody.find('.check-column input:checked').each(function() {
                jQuery(this).closest('tr').hide().find('input').val('');
              });

              return false;
            });

            // Ordering
            jQuery('.abf_services tbody').sortable({
              items:'tr',
              cursor:'move',
              axis:'y',
              handle: '.sort',
              scrollSensitivity:40,
              forcePlaceholderSize: true,
              helper: 'clone',
              opacity: 0.65,
              placeholder: 'wc-metabox-sortable-placeholder',
              start:function(event,ui){
                ui.item.css('baclbsround-color','#f6f6f6');
              },
              stop:function(event,ui){
                ui.item.removeAttr('style');
                abf_services_row_indexes();
              }
            });

            function abf_services_row_indexes() {
              jQuery('.abf_services tbody tr').each(function(index, el){
                jQuery('input.order', el).val( parseInt( jQuery(el).index('.abf_services tr') ) );
              });
            };

          });

        </script>
      </td>
    </tr>
    <?php
    return ob_get_clean();
  }


  /**
   * validate_single_select_country_field function.
   *
   * @access public
   * @param mixed $key
   * @return void
   */
  public function validate_single_select_country_field( $key ) {

    if ( isset( $_POST['woocommerce_abf_origin_country_state'] ) )
      return $_POST['woocommerce_abf_origin_country_state'];
    return '';
  }
  
  /**
   * validate_box_packing_field function.
   *
   * @access public
   * @param mixed $key
   * @return void
   */
  public function validate_box_packing_field( $key ) {

    $boxes = array();

    if ( isset( $_POST['boxes_outer_length'] ) ) {
      $boxes_outer_length = $_POST['boxes_outer_length'];
      $boxes_outer_width  = $_POST['boxes_outer_width'];
      $boxes_outer_height = $_POST['boxes_outer_height'];
      $boxes_inner_length = $_POST['boxes_inner_length'];
      $boxes_inner_width  = $_POST['boxes_inner_width'];
      $boxes_inner_height = $_POST['boxes_inner_height'];
      $boxes_box_weight   = $_POST['boxes_box_weight'];
      $boxes_max_weight   = $_POST['boxes_max_weight'];


      for ( $i = 0; $i < sizeof( $boxes_outer_length ); $i ++ ) {

        if ( $boxes_outer_length[ $i ] && $boxes_outer_width[ $i ] && $boxes_outer_height[ $i ] && $boxes_inner_length[ $i ] && $boxes_inner_width[ $i ] && $boxes_inner_height[ $i ] ) {

          $boxes[] = array(
            'outer_length' => floatval( $boxes_outer_length[ $i ] ),
            'outer_width'  => floatval( $boxes_outer_width[ $i ] ),
            'outer_height' => floatval( $boxes_outer_height[ $i ] ),
            'inner_length' => floatval( $boxes_inner_length[ $i ] ),
            'inner_width'  => floatval( $boxes_inner_width[ $i ] ),
            'inner_height' => floatval( $boxes_inner_height[ $i ] ),
            'box_weight'   => floatval( $boxes_box_weight[ $i ] ),
            'max_weight'   => floatval( $boxes_max_weight[ $i ] ),
          );

        }

      }

    }

    return $boxes;
  }

  /**
   * init_form_fields function.
   *
   * @access public
   * @return void
   */
  public function init_form_fields() {
    global $woocommerce;

    $this->form_fields  = array(
      'enabled'          => array(
        'title'           => __( 'Enable/Disable', 'wc_abf' ),
        'type'            => 'checkbox',
        'label'           => __( 'Enable this shipping method', 'wc_abf' ),
        'default'         => 'no'
      ),
      'title'            => array(
        'title'           => __( 'Method Title', 'wc_abf' ),
        'type'            => 'text',
        'description'     => __( 'This controls the title which the user sees during checkout.', 'wc_abf' ),
        'default'         => __( 'ABF', 'wc_abf' )
      ),
      'availability'  => array(
        'title'           => __( 'Method Availability', 'wc_abf' ),
        'type'            => 'select',
        'default'         => 'all',
        'class'           => 'availability',
        'options'         => array(
          'all'            => __( 'All Countries', 'wc_abf' ),
          'specific'       => __( 'Specific Countries', 'wc_abf' ),
        ),
      ),
      'countries'        => array(
        'title'           => __( 'Specific Countries', 'wc_abf' ),
        'type'            => 'multiselect',
        'class'           => 'chosen_select',
        'css'             => 'width: 450px;',
        'default'         => '',
        'options'         => $woocommerce->countries->get_allowed_countries(),
      ),
      'api'           => array(
        'title'           => __( 'API Settings', 'wc_abf' ),
        'type'            => 'title',
        'description'     => __( 'You need to obtain ABF account credentials by registering on via their website.', 'wc_abf' ),
      ),
      'user_id'           => array(
        'title'           => __( 'ABF User ID', 'wc_abf' ),
        'type'            => 'text',
        'description'     => __( 'Obtained from ABF after getting an account.', 'wc_abf' ),
        'default'         => '',
      ),
      'password'            => array(
        'title'           => __( 'ABF Password', 'wc_abf' ),
        'type'            => 'text',
        'description'     => __( 'Obtained from ABF after getting an account.', 'wc_abf' ),
        'default'         => '',
      ),
      'secure_id'          => array(
        'title'           => __( 'ABF Access Key', 'wc_abf' ),
        'type'            => 'text',
        'description'     => __( 'Obtained from ABF after getting an account.', 'wc_abf' ),
        'default'         => '',
      ),
      'origin_city'      => array(
        'title'           => __( 'Origin City', 'wc_abf' ),
        'type'            => 'text',
        'description'     => __( 'Enter the city for the <strong>sender</strong>.', 'wc_abf' ),
        'default'         => '',
      ),
      'origin_zip'      => array(
        'title'           => __( 'Origin ZIP', 'wc_abf' ),
        'type'            => 'text',
        'description'     => __( 'Enter the zip/postcode for the <strong>sender</strong>.', 'wc_abf' ),
        'default'         => '',
      ),
      'origin_country_state'      => array(
        'type'            => 'single_select_country',
      ),
      'debug'  => array(
        'title'           => __( 'Debug Mode', 'wc_abf' ),
        'label'           => __( 'Enable debug mode', 'wc_abf' ),
        'type'            => 'checkbox',
        'default'         => 'no',
        'description'     => __( 'Enable debug mode to show debugging information on your cart/checkout.', 'wc_abf' )
      ),
      'offer_rates'   => array(
        'title'           => __( 'Offer Rates', 'wc_abf' ),
        'type'            => 'select',
        'description'     => '*This currently has no effect.',
        'default'         => 'all',
        'options'         => array(
          'all'         => __( 'Offer the customer all returned rates', 'wc_abf' ),
          'cheapest'    => __( 'Offer the customer the cheapest rate only', 'wc_abf' ),
        ),
      ),
      'default_commodity_class' => array(
        'title'           => __('Default Commoditiy Class', 'wc_abf'),
        'type'            => 'select',
        'description'     => 'If a product does not have a Cover attribute set, this will be used for the shipment class.',
        'default'         => '65',
        'options'         => array(
          '50'            => '50',
          '55'            => '55',
          '60'            => '60',
          '65'            => '65',
          '70'            => '70',
          '77.5'          => '77.5',
          '85'            => '85',
          '92.5'          => '92.5',
          '100'           => '100',
          '110'           => '110',
          '125'           => '125',
          '150'           => '150',
          '175'           => '175',
          '200'           => '200',
          '250'           => '250',
          '300'           => '300',
          '400'           => '400',
          '500'           => '500'
        )
      ),
      'fallback' => array(
        'title'       => __( 'Fallback', 'wc_abf' ),
        'type'        => 'text',
        'description' => __( 'If ABF returns no matching rates, offer this amount for shipping so that the user can still checkout. Leave blank to disable.', 'wc_abf' ),
        'default'     => ''
      ),
      'packing_method'  => array(
        'title'           => __( 'Parcel Packing Method', 'wc_abf' ),
        'type'            => 'select',
        'default'         => '',
        'class'           => 'packing_method',
        'options'         => array(
          'per_item'       => __( 'Default: Pack items individually', 'wc_abf' ),
          'box_packing'    => __( 'Pack into boxes with weights and dimensions', 'wc_abf' ),
        ),
      ),
      'boxes'  => array(
        'type'            => 'box_packing'
      ),
    );
  }

  /**
   * calculate_shipping function.
   *
   * @access public
   * @param mixed $package
   * @return void
   */
  public function calculate_shipping( $package ) {
    global $woocommerce;

    libxml_use_internal_errors( true );

    // Only return rates if the package has a destination including country, postcode
    if ( ( ''==$package['destination']['country'] ) || ( ''==$package['destination']['postcode'] ) ) {
      if ( $this->debug ) {
        $woocommerce->add_message( __('ABF: Country, or Zip not yet supplied. Rates not requested.', 'wc_abf') );
      }
      return; 
    }

    $rate_request = $this->endpoint . '?' . http_build_query($this->get_rate_request( $package ));


    $response = wp_remote_retrieve_body(
      wp_remote_get( $rate_request, array(
          'timeout'   => 70,
          'sslverify' => 0
        )
      )
    );

    if ( $this->debug ) {
      $woocommerce->add_message( 'ABF REQUEST: <pre>' . htmlspecialchars( print_r( $rate_request, true ) ) . '</pre>' );
      $woocommerce->add_message( 'ABF RESPONSE: <pre>' . htmlspecialchars( print_r( $response, true ) ) . '</pre>' );
    }


    // parse the results
    $xml = simplexml_load_string( preg_replace('/<\?xml.*\?>/','', $response ) );

    if ( $this->debug ) {
      if ( ! $xml ) {
        $woocommerce->add_error( __( 'Failed loading XML', 'wc_abf' ) );
      }
    }               

    if ( $xml->NUMERRORS == 0 ) {

      $rate_cost = (float) $xml->CHARGE;

      $rate_id     = $this->id;
      $rate_name   = $this->title;

      $rate = array(
        'id'    => $rate_id,
        'label' => $rate_name,
        'cost'  => $rate_cost,
        'sort'  => $sort
      );

    } else {
      // Either there was an error on this rate, or the rate is not valid (i.e. it is a domestic rate, but shipping international)
      if ( $this->debug ) {

        $woocommerce->add_error( sprintf( __( '[ABF] Errors returned in request: (%s) %s', 'wc_abf' ),
                  $xml->ERROR->ERRORCODE,
                  $xml->ERROR->ERRORMESSAGE ) );
      }
    }

    // Add rates
    if ( $rate ) {
      $this->add_rate($rate);
      /*if ( $this->offer_rates == 'all' ) {

        uasort( $rates, array( $this, 'sort_rates' ) );
        foreach ( $rates as $key => $rate ) {
          $this->add_rate( $rate );
        }

      } else {

        $cheapest_rate = '';

        foreach ( $rates as $key => $rate ) {
          if ( ! $cheapest_rate || $cheapest_rate['cost'] > $rate['cost'] )
            $cheapest_rate = $rate;
        }

        $cheapest_rate['label'] = $this->title;

        $this->add_rate( $cheapest_rate );

      }*/
    // Fallback
    } elseif ( $this->fallback ) {
      $this->add_rate( array(
        'id'    => $this->id . '_fallback',
        'label' => $this->title,
        'cost'  => $this->fallback,
        'sort'  => 0
      ) );
      if ( $this->debug ) {
        $woocommerce->add_message( __('ABF: Using Fallback setting.', 'wc_abf') );
      }
    }
  }

  /**
   * sort_rates function.
   *
   * @access public
   * @param mixed $a
   * @param mixed $b
   * @return void
   */
  public function sort_rates( $a, $b ) {
    if ( $a['sort'] == $b['sort'] ) return 0;
    return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
  }

  /**
   * get_package_requests
   *
   *
   *
   * @access private
   * @return void
   */
  private function get_package_requests( $package ) {

    // Choose selected packing
    switch ( $this->packing_method ) {
      case 'box_packing' :
        $requests = $this->box_shipping( $package );
        break;
      case 'per_item' :
      default :
        $requests = $this->per_item_shipping( $package );
        break;
    }

    return $requests;
  }

  /**
   * get_rate_request
   *
   * Get rate requests for all
   * @access private
   * @return associative array of strings, id est, key-value pairs
   *
   */
  private function get_rate_request( $package ) {
    $package_requests = $this->get_package_requests( $package );
    if(!$package_requests) {
      return;
    }
    global $woocommerce;
    $date = getdate();
    $customer = $woocommerce->customer;
    $weight = 0;

    $ctr = 0;
    foreach ( $package['contents'] as $item_id => $values ) {
      $ctr++;

      if ( ! $values['data']->needs_shipping() ) {
        if ( $this->debug )
          $woocommerce->add_message( sprintf( __( 'Product #%d is virtual. Skipping.', 'wc_abf' ), $ctr ) );
        continue;
      }

      if ( ! $values['data']->get_weight() ) {
        if ( $this->debug )
          $woocommerce->add_error( sprintf( __( 'Product #%d is missing weight. Aborting.', 'wc_abf' ), $ctr ) );
        return;
      }

      $weight += woocommerce_get_weight( $values['data']->get_weight(), 'lbs' );
      $qty = $values['quantity'];
      if($qty) {
        $weight *= $qty;
      }
    }

    return array_merge(array(
      'ID'          => $this->secure_id,
      'ShipAff'     => 'Y',
      'ShipCity'    => $this->origin_city,
      'ShipState'   => $this->origin_state,
      'ShipZip'     => $this->origin_zip,
      'ShipCounry'  => $this->origin_country,
      'ConsCity'    => $package['destination']['city'],
      'ConsState'   => $package['destination']['state'],
      'ConsZip'     => $package['destination']['postcode'],
      'ConsCountry' => $package['destination']['country'],
      'ShipMonth'   => $date['mon'],
      'ShipDay'     => $date['mday'],
      'ShipYear'    => $date['year']
    ), $package_requests);
  }


  /**
   * per_item_shipping function.
   *
   * @access private
   * @param mixed $package
   * @return mixed $requests - an associative array / key-value pairs
   */
  private function per_item_shipping( $package ) {
    global $woocommerce;

    $requests = array();

    $ctr=0;
    $cube = 0;
    foreach ( $package['contents'] as $item_id => $values ) {
      $ctr++;
      if ( ! $values['data']->needs_shipping() ) {
        if ( $this->debug )
          $woocommerce->add_message( sprintf( __( 'Product #%d is virtual. Skipping.', 'wc_abf' ), $ctr ) );
        continue;
      }

      if ( ! $values['data']->get_weight() ) {
        if ( $this->debug )
          $woocommerce->add_error( sprintf( __( 'Product #%d is missing weight. Aborting.', 'wc_abf' ), $ctr ) );
        return;
      }

      // get package weight
      $weight = woocommerce_get_weight( $values['data']->get_weight(), 'lbs' );

      // get quantity in cart
      $cart_item_qty = $values['quantity'];
      
      // get package dimensions
      if ( $values['data']->length && $values['data']->height && $values['data']->width ) {

        $dimensions = array(  woocommerce_get_dimension( $values['data']->length, 'in' ),
                   woocommerce_get_dimension( $values['data']->height, 'in' ),
                   woocommerce_get_dimension( $values['data']->width, 'in' ) );
        $cube += $cart_item_qty * ($dimensions[0] * $dimensions[1] * $dimensions[2]) / 1728.0;
        // 1728 = 12^3
      }

      // get weight, or 1 if less than 1 lbs.
      $weight *= $cart_item_qty;
      $_weight = ( floor( $weight ) < 1 ) ? 1 : floor($weight);

      $requests['Wgt' . $ctr] = $_weight;
      $requests['Class' . $ctr] = $values['data']->get_shipping_class();
      if(!$requests['Class' . $ctr])
        $requests['Class' . $ctr] = $this->default_commodity_class;
      $requests['Cube' . $ctr] = $cube;
    }

  return $requests;
  }

  /**
   * box_shipping function.
   *
   * @access private
   * @param mixed $package
   * @return void
   */
  private function box_shipping( $package ) {
    global $woocommerce;

    $requests = array();
    $commodity_class = 0;

    if ( ! class_exists( 'WC_Boxpack' ) )
      include_once 'box-packer/class-wc-boxpack.php';

    $boxpack = new WC_Boxpack();

    // Define boxes
    if ( ! empty( $this->boxes ) ) {
      foreach ( $this->boxes as $box ) {

        $newbox = $boxpack->add_box( $box['outer_length'], $box['outer_width'], $box['outer_height'], $box['box_weight'] );

        $newbox->set_inner_dimensions( $box['inner_length'], $box['inner_width'], $box['inner_height'] );

        if ( $box['max_weight'] )
          $newbox->set_max_weight( $box['max_weight'] );

      }
    }

    // Add items
    $ctr = 0;
    foreach ( $package['contents'] as $item_id => $values ) {
      $ctr++;

      if ( ! $values['data']->needs_shipping() ) {
        if ( $this->debug )
          $woocommerce->add_message( sprintf( __( 'Product #%d is virtual. Skipping.', 'wc_abf' ), $ctr ) );
        continue;
      }

      $tmp = $values['data']->get_shipping_class();
      if(!$tmp)
        $tmp = $this->default_commodity_class;
      if($tmp > $commodity_class)
        $commodity_class = $tmp;

      if ( $values['data']->length && $values['data']->height && $values['data']->width && $values['data']->weight ) {

        $dimensions = array( $values['data']->length, $values['data']->height, $values['data']->width );

        for ( $i = 0; $i < $values['quantity']; $i ++ ) {
          $boxpack->add_item(
            number_format( woocommerce_get_dimension( $dimensions[2], 'in' ), 2, '.', ''),
            number_format( woocommerce_get_dimension( $dimensions[1], 'in' ), 2, '.', ''),
            number_format( woocommerce_get_dimension( $dimensions[0], 'in' ), 2, '.', ''),
            number_format( woocommerce_get_weight( $values['data']->get_weight(), 'lbs' ), 2, '.', ''),
            $values['data']->get_price()
          );
        }

      } else {
          if ( $this->debug )
          $woocommerce->add_error( sprintf( __( 'ABF Parcel Packing Method is set to Pack into Boxes. Product #%d is missing dimensions. Aborting.', 'wc_abf' ), $ctr ) );
        return;
      }
    }

    // Pack it
    $boxpack->pack();

    // Get packages
    $box_packages = $boxpack->get_packages();

    $ctr=0;
    $weight=0;
    $cube=0;
    foreach ( $box_packages as $key => $box_package ) {
      $ctr++;

      if ( $this->debug )
        $woocommerce->add_error( "PACKAGE " . $ctr . " (" . $key . ")\n<pre>" . print_r( $box_package,true ) . "</pre>");

      $weight    += $box_package->weight;
      $dimensions = array( $box_package->length, $box_package->width, $box_package->height );
      $cube += ($dimensions[0] * $dimensions[1] * $dimensions[2]) / 1728.0;
      sort( $dimensions );
    }
    $requests['Wgt1'] = ( floor( $weight ) < 1 ) ? 1 : floor($weight);
    $requests['Class1'] = $commodity_class;
    $requests['Cube1'] = $cube;

    return $requests;
  }
}
